package net;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import com.apple.dnssd.*;

/**
 * This class represents a service your application can advertise on the network and wait for 
 * clients to find it and connect to it. Instead of a specific port, you need to pass a "name"
 * of the service. This name has to be of the form "_<service-name>._<connection-type>".
 * The service name can be anything alphanumeric term. The connection type can be somthing like
 * tcp or udp. An example: "_myservice._tcp".
 * After the initialization of the NetService object with a specific name it needs to be
 * registered on the network using it's register() method. To start listening to clients, the 
 * service needs to be started by calling the start() method.
 * After that, the delegate object of the service will be notified when a new client connected
 * to it. The service will automatically create a NetConnection object, that can be used to 
 * communicate with the client and pass it to the delegate.
 */
public class NetService implements RegisterListener {
	private DNSSDRegistration _serviceRecord;
	private boolean _registered;
	private String _type;
	private int _port;
	private ServerSocket _socket;
	private ServiceListeningThread _thread;
	private boolean _running = false;
	private boolean _shouldRun = false;
	public NetServiceDelegate delegate;
	
	public int port(){
		return _port;
	}
 
	public boolean isRegistered(){
		return _registered;
	}
	
	public String getName(){
		return _type;
	}
	
	public NetService(String type) throws IOException{
		_type = type;
		_socket = new ServerSocket(0); // Create server socket on a free port.
		_port = _socket.getLocalPort();
	}
	
	public NetService(String type, int port) throws IOException{
		_type = type;
		_port = port;
		_socket = new ServerSocket(port);
	}
	
	public NetService(String type, int port, NetServiceDelegate myDelegate) throws IOException{
		_type = type;
		_port = port;
		_socket = new ServerSocket(port);
		delegate = myDelegate;
	}
 
	public void register()  {
		try {
			_serviceRecord = DNSSD.register(0, 0, null, _type, null, null, _port, null, this);
		} catch (DNSSDException e) {
			NetLogger.exception("Couldn't register service on port " + _port + ".");
		}
	}
 
	public void unregister(){
		_serviceRecord.stop();
		_registered = false;
	}
 
	public void serviceRegistered(DNSSDRegistration registration, int flags,String serviceName, String regType, String domain){
		NetLogger.service("Service registered on port " + _port + ".");
		_registered = true;
		if(_shouldRun) {
			startListeningThread();
		}
	}
 
	public void operationFailed(DNSSDService registration, int error){
		NetLogger.exception("Couldn't register service on port " + _port + ".");
	}
	
	private void startListeningThread(){
		if(_thread == null) {
			_thread = new ServiceListeningThread(this);
			_thread.start();
		}
		_running = true;
		NetLogger.service("Service started.");
	}
	
	public void start(){
		if(_running || _shouldRun) return;  // Don't do anything, if thread is running already.
		if(_registered){
			startListeningThread();
		} else {
			_shouldRun = true;
		}
	}
	
	public void stop() {
		_running = false;
		_shouldRun = false;
		NetLogger.service("Service stopped.");
	}
	
	class ServiceListeningThread extends Thread {
		private NetService _service;
		public ServiceListeningThread(NetService service){
			_service = service;
		}
		public void run()
		{
			NetLogger.service("Waiting for connections...");
			try {
				while (_running) {
					Socket socket =_socket.accept();
					if(_running && delegate != null){
						NetLogger.connection("Client connecting...");
						NetConnection connection = new NetConnection(socket);
						delegate.connectionMadeToNetService(_service, connection);
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
}